package http

import (
	"power/core/common/errorcode"
	"power/core/localwrap"
	"power/http/graphroute"
	"power/http/userroute"
	"time"

	"github.com/RocksonZeta/irisx"
	"github.com/RocksonZeta/wrap/errs"
	"github.com/RocksonZeta/wrap/utils/mathutil"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"
	"github.com/kataras/iris/v12/core/router"
)

var log = localwrap.GetLogger().Fork("power/http", "")

type SessionProvider struct {
}

const sessionTTL = 3600
const sessionKey = "sessionid"

func (s *SessionProvider) GetSessionId(ctx *irisx.Context) string {
	sid := ctx.Values().GetString("__sessionid__")
	if sid != "" {
		return sid
	}
	return ctx.GetCookie(sessionKey)
}
func (s *SessionProvider) SetSessionId(ctx *irisx.Context) {
	sid := mathutil.RandomStr(32, false)
	ctx.Values().Set("__sessionid__", sid)
	ctx.SetCookieLocal(sessionKey, sid, sessionTTL, true, "")
}
func (s *SessionProvider) Set(key string, value interface{}, secs int) error {
	red := localwrap.GetRedis()
	defer red.Close()
	red.SetJson(key, value, secs)
	return nil
}
func (s *SessionProvider) Get(key string, result interface{}) error {
	red := localwrap.GetRedis()
	defer red.Close()
	red.GetJson(key, result)
	return nil
}
func (s *SessionProvider) Refresh(key string, secs int) error {
	red := localwrap.GetRedis()
	defer red.Close()
	red.Expire(key, time.Duration(secs)*time.Second)
	return nil
}
func (s *SessionProvider) Remove(key string) error {
	red := localwrap.GetRedis()
	defer red.Close()
	red.Del(key)
	return nil
}
func (s *SessionProvider) UidKey() string {
	return "uid"
}

func NewApp() *iris.Application {
	app := iris.New()
	app.HandleDir("/static", "static", router.DirOptions{IndexName: "-", Gzip: true})
	app.ContextPool.Attach(func() context.Context {
		return &irisx.Context{
			Context:         context.NewContext(app),
			SessionProvider: &SessionProvider{},
		}
	})
	app.Use(ErrorFilter)
	app.Use(SidFilter)
	app.Use(SigninFilter)
	tpl := iris.HTML("view", ".html")
	irisx.Enhance(tpl)
	// tpl.Delims("<%", "%>") // conflict with vue
	tpl.Reload(localwrap.Config.DevMode)
	app.RegisterView(tpl)

	// app.Get("/", func(ctx iris.Context) {
	// 	c := ctx.(*iriswrap.Context)
	// 	c.Ok(c.Session.Uid())
	// })
	route(app)
	return app
}

func route(app *iris.Application) {
	app.Layout(iris.NoLayout)
	app.Get("/graph/{id:int}", func(ctx iris.Context) {
		ctx.View("graph/powernetEditor.html")
	})
	// publicroute.RouteIndex(app)
	userroute.Route(app.Party("/api/user"))
	graphroute.Route(app.Party("/api/project"))
	graphroute.RouteGraphs(app.Party("/api/graph"))
	// graphroute.RouteGraphUsers(app.Party("/graph"))
	app.Get("/{path:path}", func(ctx iris.Context) {
		// c := ctx.(*irisx.Context)
		path := ctx.Params().GetString("path")
		log.Debug().Str("path", path).Send()
		ctx.ServeFile("static/index.html", false)
	})
}

var skipSigninUrls = map[string]bool{
	"/":                 true,
	"/api/user/signin":  true,
	"/api/user/signout": true,
}

func SigninFilter(ctx iris.Context) {
	c := ctx.(*irisx.Context)
	path := c.Path()
	if c.GetUidInt() == 0 && !skipSigninUrls[path] {
		c.JSON(errs.NewUserError(errorcode.UserNoSignin, "Please signin."))
		c.EndRequest()
		return
	}
	c.Next()
}

func SidFilter(ctx iris.Context) {
	c := ctx.(*irisx.Context)
	sid := c.Sid()
	if sid != "" {
		ctx.Next()
		return
	}
	c.SessionProvider.SetSessionId(c)
	ctx.Next()
}
func ErrorFilter(ctx iris.Context) {
	c := ctx.(*irisx.Context)
	log.Debug().Func("ErrorFilter").Str("method", c.Method()).Str("path", c.Path()).Send()
	defer func() {
		if r := recover(); r != nil {
			if err, ok := r.(*errs.Err); ok {
				if err.State == 0 {
					log.Error().Err(err).Stack().Msg(err.Error())
				}
				c.JSON(err)
				return
			}
			if err, ok := r.(error); ok {
				log.Error().Err(err).Stack().Msg(err.Error())
				c.Err(errorcode.InternalError, err.Error())
				return
			}
			c.JSON(r)
		}
	}()
	c.Next()
}
